using System;
using System.Collections;
using System.IO;
using System.Xml;
using System.Web.Util;

using System.Xml.Serialization;
using System.Collections.Generic;
using System.Text;
using Microsoft.SharePoint;

using MOSSExtractor;

namespace cmdMOSSExtractor
{
    class Program
    {
        public static void DumpException(Exception ex)
        {

            WriteExceptionInfo(ex);
            if (null != ex.InnerException)
            {
                WriteExceptionInfo(ex.InnerException);
            }

        }

        public static void WriteExceptionInfo(Exception ex)
        {

            Console.WriteLine("--------- Exception Data ---------");

            Console.WriteLine("Message: {0}", ex.Message);
            Console.WriteLine("Exception Type: {0}", ex.GetType().FullName);

            Console.WriteLine("Source: {0}", ex.Source);

            Console.WriteLine("StrackTrace: {0}", ex.StackTrace);

            Console.WriteLine("TargetSite: {0}", ex.TargetSite);
        }


        static void Main(string[] args)
        {
            try
            {

                if (args.Length != 8)
                {
                    Console.WriteLine("Usage {0} -url SiteCollectionURL -contenttype ContentTypeName/ContentTypeID -directory OutputDirectory -featurenamespace FeatureNamespace", Environment.CommandLine);
                    return;
                }
                Parameters parameters = new Parameters();
                if (!parameters.ReadParameters(args))
                {
                    Console.WriteLine("Errors\r\nUsage {0} -url SiteCollectionURL -contenttype ContentTypeName/ContentTypeID -directory OutputDirectory -featurenamespace FeatureNamespace", Environment.CommandLine);
                    return;
                }

                string siteCollectionUrl = parameters.Url;
                string contenttypeNameorID = parameters.ContenttypeName;
                string dirname = parameters.OutputDirectory;
                string featureNamespaceRoot = parameters.FeatureNamespaceRoot;
                if (!Directory.Exists(dirname))
                    Directory.CreateDirectory(dirname);

                Directory.SetCurrentDirectory(dirname);
                Directory.CreateDirectory(featureNamespaceRoot + ".ContentType");
                SPContentTypeId cid;
                bool ColumnsAdded = false;
                bool ContentTypeFound = false;


                Solution solution = new Solution();
                solution.SolutionId = Guid.NewGuid().ToString();
                FeatureManifestReference featureref = new FeatureManifestReference();
                featureref.Location = featureNamespaceRoot + ".ContentType\\feature.xml";
                solution.FeatureManifests.Add(featureref);

                FeatureDefinition featContentTypes = new FeatureDefinition();
                featContentTypes.Id = Guid.NewGuid().ToString();
                featContentTypes.Scope = "Site";
                ElementManifestDefinition contentTypeDefinition = new ElementManifestDefinition();

                contentTypeDefinition.Location = "contenttypes.xml";
                featContentTypes.ElementManifests.Add(contentTypeDefinition);
                Elements ContentTypeElements = new MOSSExtractor.Elements();
                FeatureDefinition featColumns = new FeatureDefinition();
                ElementManifestDefinition columnDefinition = new ElementManifestDefinition();
                Elements ColumnElements = new MOSSExtractor.Elements();

                bool fileSaved = false;
                string fName = "";

                using (SPSite site = new SPSite(siteCollectionUrl))
                {
                    using (SPWeb web = site.OpenWeb())
                    {
                        int count = web.AvailableContentTypes.Count;
                        SPContentType contentTypeToBeExported = null;
                        if (contenttypeNameorID.StartsWith("0x"))
                        {
                            cid = new SPContentTypeId(contenttypeNameorID);
                            contentTypeToBeExported = web.AvailableContentTypes[cid];
                            if (contentTypeToBeExported != null)
                            {
                                ContentTypeFound = true;
                                contenttypeNameorID = contentTypeToBeExported.Name;
                            }
                        }


                        SPContentType parentCT;
                        if (!ContentTypeFound)
                        {
                            foreach (SPContentType ct in web.AvailableContentTypes)
                            {
                                if (contenttypeNameorID != ct.Name)
                                    continue;

                                ContentTypeFound = true;
                                contentTypeToBeExported = ct;
                            }
                        }
                        if (contentTypeToBeExported != null)
                        {
                            contenttypeNameorID = contentTypeToBeExported.Name;
                            featContentTypes.Title = contenttypeNameorID;

                            Console.WriteLine("Content type= " + contentTypeToBeExported.Name);

                            ContentType ctx = new ContentType();
                            fName = featureNamespaceRoot + ".ContentType\\" + contentTypeToBeExported.DocumentTemplate;
                            parentCT = contentTypeToBeExported.Parent;
                            ctx.ID = contentTypeToBeExported.Id.ToString(); ;
                            ctx.Name = contentTypeToBeExported.Name;
                            ctx.Description = contentTypeToBeExported.Description;
                            ctx.Group = contentTypeToBeExported.Group;
                            ctx.Hidden = contentTypeToBeExported.Hidden.ToString();
                            ctx.ReadOnly = contentTypeToBeExported.ReadOnly.ToString();
                            ctx.Version = contentTypeToBeExported.Version.ToString();

                            DocumentTemplate dt = new DocumentTemplate();
                            dt.TargetName = contentTypeToBeExported.DocumentTemplate;
                            ctx.DocumentTemplates.Add(dt);
                            try
                            {
                                SPFolder cts = web.Folders["_cts"];
                                SPFolder proposalFolder = cts.SubFolders[contentTypeToBeExported.Name];
                                if (proposalFolder.Files.Count > 0)
                                {
                                    SPFile file = proposalFolder.Files[contentTypeToBeExported.DocumentTemplate];

                                    File.WriteAllBytes(dirname + "\\" + fName, file.OpenBinary());

                                    fileSaved = true;
                                    ElementManifestDefinitionFile filedef = new ElementManifestDefinitionFile();

                                    filedef.Location = contentTypeToBeExported.DocumentTemplate;
                                    featContentTypes.ElementManifests.Add(filedef);
                                    ModuleDefinition module = new ModuleDefinition();
                                    module.Name = ctx.Group;
                                    module.Url = "_cts/" + ctx.Name;
                                    FileDefinition fdef = new FileDefinition();
                                    fdef.Url = contentTypeToBeExported.DocumentTemplate;
                                    fdef.Name = contentTypeToBeExported.DocumentTemplate;
                                    fdef.Type = "Ghostable";
                                    module.Files.Add(fdef);

                                    ContentTypeElements.Modules.Add(module);
                                }

                            }
                            catch (Exception templateException)
                            {
                                Console.WriteLine(templateException.Message);
                            }


                            ContentTypeElements.ContentType.Add(ctx);

                            foreach (SPFieldLink link in contentTypeToBeExported.FieldLinks)
                            {
                                SPFieldLink linkParent = parentCT.FieldLinks[link.Id];

                                if (linkParent != null)
                                    continue;
                                SPField thisField = web.AvailableFields[link.Id];
                                SharedFieldDefinition sharedField = new SharedFieldDefinition();
                                sharedField.Hidden = link.Hidden.ToString();
                                sharedField.ID = link.Id.ToString("B");
                                sharedField.Required = link.Required.ToString();
                                sharedField.Sealed = false.ToString();
                                sharedField.StaticName = thisField.InternalName;
                                sharedField.Type = thisField.TypeAsString;
                                switch (sharedField.Type.ToLower())
                                {
                                    case "choice":
                                        {
                                            SPFieldChoice cf = (SPFieldChoice)thisField;
                                            sharedField.Choices = new ArrayList();
                                            foreach (string choice in cf.Choices)
                                            {
                                                sharedField.Choices.Add(choice);
                                            }
                                            break;
                                        }
                                    case "calculated":
                                        {
                                            SPFieldCalculated cfcalc = (SPFieldCalculated)thisField;
                                            sharedField.Formula = cfcalc.Formula;
                                            break;

                                        }
                                }
                                sharedField.Group = thisField.Group;
                                sharedField.Name = thisField.Title;
                                sharedField.DisplayName = thisField.Title;
                                sharedField.Default = thisField.DefaultValue;
                                sharedField.DefaultFormula = thisField.DefaultFormula;

                                ColumnElements.Fields.Add(sharedField);
                                FieldRefDefinition fref = new FieldRefDefinition();
                                SPField field = web.Fields[link.Id];
                                fref.Name = link.Name;
                                fref.ID = link.Id.ToString("B");
                                ctx.FieldRefs.Add(fref);
                                if (!ColumnsAdded)
                                {
                                    featColumns.Title = contenttypeNameorID + "Columns";
                                    featColumns.Id = Guid.NewGuid().ToString();
                                    featColumns.Scope = "Site";

                                    columnDefinition.Location = "columns.xml";
                                    featColumns.ElementManifests.Add(columnDefinition);
                                    featureref = new FeatureManifestReference();
                                    featureref.Location = featureNamespaceRoot + ".Column\\feature.xml";
                                    solution.FeatureManifests.Add(featureref);
                                    ColumnsAdded = true;
                                    Directory.CreateDirectory(featureNamespaceRoot + ".Column");

                                }
                            }

                        }

                    }
                }
                if (ContentTypeFound)
                {
                    if (featContentTypes.Validate())
                        featContentTypes.Serialize(dirname + "\\" + featureNamespaceRoot + ".ContentType\\feature.xml");
                    else
                        Console.WriteLine(featContentTypes.LastError);
                    ContentTypeElements.Serialize(dirname + "\\" + featureNamespaceRoot + ".ContentType\\contenttypes.xml");
                }
                if (ColumnsAdded)
                {
                    if (featColumns.Validate())
                        featColumns.Serialize(dirname + "\\" + featureNamespaceRoot + ".Column\\feature.xml");
                    else
                        Console.WriteLine(featColumns.LastError);
                    ColumnElements.Serialize(dirname + "\\" + featureNamespaceRoot + ".Column\\columns.xml");
                }

                if (ContentTypeFound)
                {
                    solution.Serialize(dirname + "\\manifest.xml");
                    StreamWriter swddf = new StreamWriter(dirname + "\\package.ddf");
                    swddf.WriteLine(".OPTION Explicit ; Generate errors");
                    swddf.WriteLine(".Set CabinetNameTemplate=\"" + contenttypeNameorID + "\".wsp");
                    swddf.WriteLine(".Set DiskDirectoryTemplate=CDROM");
                    swddf.WriteLine(".Set CompressionType=MSZIP");
                    swddf.WriteLine(".Set UniqueFiles=Off");
                    swddf.WriteLine(".Set Cabinet=On");
                    swddf.WriteLine(".Set DiskDirectory1=Package");
                    swddf.WriteLine(";");
                    swddf.WriteLine("; \\");
                    swddf.WriteLine(";**************************************************");
                    swddf.WriteLine("manifest.xml");
                    swddf.WriteLine(".Set DestinationDir=\"" + featureNamespaceRoot + ".ContentType\"");
                    swddf.WriteLine("\"" + featureNamespaceRoot + ".ContentType\\Feature.xml\"");
                    swddf.WriteLine("\"" + featureNamespaceRoot + ".ContentType\\contenttypes.xml\"");
                    if (fileSaved)
                        swddf.WriteLine("\"" + fName + "\"");

                    if (ColumnsAdded)
                    {
                        swddf.WriteLine(".Set DestinationDir=\"" + featureNamespaceRoot + ".Column\"");
                        swddf.WriteLine("\"" + featureNamespaceRoot + ".Column\\Feature.xml\"");
                        swddf.WriteLine("\"" + featureNamespaceRoot + ".Column\\columns.xml\"");
                    }
                    swddf.Flush();
                    swddf.Close();
                    try
                    {
                        System.Diagnostics.ProcessStartInfo psi = new System.Diagnostics.ProcessStartInfo(@"makecab.exe");
                        psi.WorkingDirectory = dirname;
                        psi.Arguments = "/f package.ddf";
                        psi.RedirectStandardOutput = true;
                        psi.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;
                        psi.UseShellExecute = false;
                        System.Diagnostics.Process listFiles;
                        listFiles = System.Diagnostics.Process.Start(psi);
                        System.IO.StreamReader myOutput = listFiles.StandardOutput;
                        listFiles.WaitForExit(2000);
                        if (listFiles.HasExited)
                        {
                            string output = myOutput.ReadToEnd();
                            Console.WriteLine(output);
                        }
                    }
                    catch (Exception CabEx)
                    {
                        Console.WriteLine("Couldn't invoke the makecab exe, please download the cabinet sdk and run the makecab.exe program from {0} directory using the parameters /f package.ddf", dirname);
                    }

                    StreamWriter sw = new StreamWriter(dirname + "\\InstallSolution.bat");
                    sw.WriteLine("\"c:\\program files\\common files\\microsoft shared\\web server extensions\\12\\bin\\stsadm.exe\" -o addsolution -filename \"" + dirname + "\\Package\\" + contenttypeNameorID + ".wsp\"");
                    sw.WriteLine("\"c:\\program files\\common files\\microsoft shared\\web server extensions\\12\\bin\\stsadm.exe\" -o deploysolution -name \"" + contenttypeNameorID + ".wsp\" -local");
                    sw.Flush();
                    sw.Close();
                    sw = new StreamWriter(dirname + "\\ActivateFeatures.bat");
                    sw.WriteLine("\"c:\\program files\\common files\\microsoft shared\\web server extensions\\12\\bin\\stsadm.exe\" -o activatefeature -filename " + featureNamespaceRoot + ".column\\feature.xml -url %1");
                    sw.WriteLine("\"c:\\program files\\common files\\microsoft shared\\web server extensions\\12\\bin\\stsadm.exe\" -o activatefeature -filename " + featureNamespaceRoot + ".contenttype\\feature.xml -url %1");
                    sw.Flush();
                    sw.Close();
                    sw = new StreamWriter(dirname + "\\DeActivateFeatures.bat");
                    sw.WriteLine("\"c:\\program files\\common files\\microsoft shared\\web server extensions\\12\\bin\\stsadm.exe\" -o deactivatefeature -filename " + featureNamespaceRoot + ".column\\feature.xml -url %1");
                    sw.WriteLine("\"c:\\program files\\common files\\microsoft shared\\web server extensions\\12\\bin\\stsadm.exe\" -o deactivatefeature -filename " + featureNamespaceRoot + ".contenttype\\feature.xml -url %1");
                    sw.Flush();
                    sw.Close();
                    sw = new StreamWriter(dirname + "\\UnInstallSolution.bat");
                    sw.WriteLine("\"c:\\program files\\common files\\microsoft shared\\web server extensions\\12\\bin\\stsadm.exe\" -o retractsolution -name \"" + contenttypeNameorID + ".wsp\" -local");
                    sw.WriteLine("\"c:\\program files\\common files\\microsoft shared\\web server extensions\\12\\bin\\stsadm.exe\" -o deletesolution -name \"" + contenttypeNameorID + ".wsp\"");
                    sw.Flush();
                    sw.Close();
                }
                else
                {
                    Console.WriteLine("Content type {0} could not be found in the site collection {1}", contenttypeNameorID, siteCollectionUrl);
                }
            }
            catch (Exception Ex)
            {
                DumpException(Ex);
            }
        }
    }
}
